//
// Created by 王筱童 on 2022/5/9.
//

#include "command_manager.h"
#include "others.h"
#include "redis_server.h"
#include <iostream>
#include<vector>
#include <random>
#include <experimental/source_location>

std::unordered_map<std::string, command_manager::meta_type> command_manager::map_functor{};
command_manager::static_init command_manager::init{};

command_manager::static_init::static_init() {
    command_manager::map_functor.insert({"SET", {&command_manager::SET, 2, true}});
    command_manager::map_functor.insert({"GET", {&command_manager::GET, 1, false}});
    command_manager::map_functor.insert({"SADD", {&command_manager::SADD, 2, true}});
    command_manager::map_functor.insert({"SCARD", {&command_manager::SCARD, 1, false}});
    command_manager::map_functor.insert({"SREM", {&command_manager::SREM, 2, true}});
    command_manager::map_functor.insert({"SCLR", {&command_manager::SCLR, 1, true}});
    command_manager::map_functor.insert({"SGET", {&command_manager::SGET, 1, false}});
    command_manager::map_functor.insert({"DEL", {&command_manager::DEL, 1, true}});
    command_manager::map_functor.insert({"EXISTS", {&command_manager::EXISTS, 1, false}});
    command_manager::map_functor.insert({"EXPRIE", {&command_manager::EXPRIE, 2, true}});
    command_manager::map_functor.insert({"PEXPRIE", {&command_manager::PEXPRIE, 2, true}});
    command_manager::map_functor.insert({"HGET", {&command_manager::HGET, 2, false}});
    command_manager::map_functor.insert({"HSET", {&command_manager::HSET, 3, true}});
    command_manager::map_functor.insert({"HEXISTS", {&command_manager::HEXISTS, 2, false}});
    command_manager::map_functor.insert({"HDEL", {&command_manager::HDEL, 2, true}});
    command_manager::map_functor.insert({"HLEN", {&command_manager::HLEN, 1, false}});
    command_manager::map_functor.insert({"HGETALL", {&command_manager::HGETALL, 1, false}});
    command_manager::map_functor.insert({"LLEN", {&command_manager::LLEN, 1, false}});
    command_manager::map_functor.insert({"LPUSH", {&command_manager::LPUSH, 2, true}});
    command_manager::map_functor.insert({"LPOP", {&command_manager::LPOP, 1, true}});
    command_manager::map_functor.insert({"LRANGE", {&command_manager::LRANGE, 3, false}});
    command_manager::map_functor.insert({"LINDEX", {&command_manager::LINDEX, 2, false}});
    command_manager::map_functor.insert({"RPUSH", {&command_manager::RPUSH, 2, true}});
    command_manager::map_functor.insert({"RPOP", {&command_manager::RPOP, 1, true}});
    command_manager::map_functor.insert({"ZADD", {&command_manager::ZADD, 2, true}});
    command_manager::map_functor.insert({"ZCARD", {&command_manager::ZCARD, 1, false}});
    command_manager::map_functor.insert({"ZRANGE", {&command_manager::ZRANGE, 3, false}});
    command_manager::map_functor.insert({"ZREM", {&command_manager::ZREM, 2, true}});
    command_manager::map_functor.insert({"ZGET", {&command_manager::ZGET, 1, true}});
    command_manager::map_functor.insert({"ZCLR", {&command_manager::ZCLR, 1, true}});
    command_manager::map_functor.insert({"MULTI", {&command_manager::MULTI, 0, false}});
    command_manager::map_functor.insert({"SAVEOF", {&command_manager::SAVEOF, 2, false}});
    command_manager::map_functor.insert({"SENTI", {&command_manager::SENTI, 1, false}});
    command_manager::map_functor.insert({"SELECT", {&command_manager::SELECT, 2, true}});
    command_manager::map_functor.insert({"SYNC", {&command_manager::SYNC, 2, false}});
    command_manager::map_functor.insert({"PSENTI", {&command_manager::PSENTI, 1, false}});
}

std::vector<std::string>
command_manager::exec_command(const std::string &command, const std::vector<std::string> &args, std::size_t index) {
    try {
        auto it = map_functor.find(command);
        if (it == map_functor.end()) {
            return {nil};
        }
        meta_type meta = it->second;
        if (args.size() < std::get<argc_index>(meta)) {
            return {nil};
        }

        functor fun = std::get<functor_index>(meta);
#ifdef TEST
        std::cout<<"exec command: "<<command<<" "<<join(args,' ')<<" "<<index<<'\n';
#endif
        auto res = fun(this, args, index);

        if (std::get<synable_index>(meta)) {
            //todo: 同步数据
            server->slaves.async_sync(command,args,server->connections[index]->db_seq());
            server->save.save(command,args,server->connections[index]->db_seq());
        }
        return res;
    } catch (const std::exception &ec) {
        std::cerr << "error: "<<std::experimental::source_location::current().file_name()<<" "<<std::experimental::source_location::current().function_name()<<" " << ec.what() << "\n";
        return {nil};
    }
}

command_manager::command_manager(redis_server *_server) : server(_server) {

}

std::vector<std::string> command_manager::SET(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.SET(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::GET(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    auto res=db.GET(args[0]);
    if(res.first){
        return {res.second};
    }
    return {nil};
}

std::vector<std::string> command_manager::DEL(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.DEL(args[0])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::EXISTS(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.EXISTS(args[0])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::EXPRIE(const std::vector<std::string> &, std::size_t) {
    return {ok};
}

std::vector<std::string> command_manager::PEXPRIE(const std::vector<std::string> &, std::size_t) {
    return {ok};
}

std::vector<std::string> command_manager::HGET(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    auto res=db.HGET(args[0],args[1]);
    if(res.first){
        return {res.second};
    }
    return {nil};
}

std::vector<std::string> command_manager::HSET(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.HSET(args[0],args[1],args[2])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::HEXISTS(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.HEXISTS(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::HDEL(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.HDEL(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::HLEN(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    auto res=std::to_string(db.HLEN(args[0]));
    return {res};
}

std::vector<std::string> command_manager::HGETALL(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    return db.HGETALL(args[0]);
}

std::vector<std::string> command_manager::LLEN(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    auto res=std::to_string(db.LLEN(args[0]));
    return {res};
}

std::vector<std::string> command_manager::LPUSH(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.LPUSH(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::LPOP(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.LPOP(args[0])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::LRANGE(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    return db.LRANGE(args[0], convert<std::size_t>(args[1]), convert<std::size_t>(args[2]));
}

std::vector<std::string> command_manager::LINDEX(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    return {db.LINDEX(args[0], convert<std::size_t>(args[1]))};
}

std::vector<std::string> command_manager::RPUSH(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.RPUSH(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::RPOP(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.RPOP(args[0])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::SADD(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.SADD(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::SCARD(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    return {std::to_string(db.SCARD(args[0]))};
}

std::vector<std::string> command_manager::SREM(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.SREM(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::SCLR(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.SCLR(args[0])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::SGET(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    return (db.SGET(args[0]));
}

std::vector<std::string> command_manager::ZADD(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.ZADD(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::ZCARD(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    return {std::to_string(db.ZCARD(args[0]))};
}

std::vector<std::string> command_manager::ZRANGE(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    return (db.ZRANGE(args[0],args[1],args[2]));
}

std::vector<std::string> command_manager::ZREM(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.ZREM(args[0],args[1])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::ZGET(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    return db.ZGET(args[0]);
}

std::vector<std::string> command_manager::ZCLR(const std::vector<std::string> &args, std::size_t index) {
    auto &conn = server->connections[index];
    auto &db = server->dbs[conn->db_seq()];
    if(db.ZCLR(args[0])){
        return {ok};
    }
    return {nil};
}

std::vector<std::string> command_manager::MULTI(const std::vector<std::string>&args, std::size_t index) {
    std::size_t now=0;
    std::vector<std::string> res;
    while (now<args.size()){
        auto num= convert<std::size_t>(args[now]);
        now++;
        if(num>0){
            auto tmp=exec_command(args[now],{args.begin()+1+now,args.begin()+now+1+num},index);
            res.insert(res.end(),tmp.begin(),tmp.end());
        }
        now+=1+num;
    }
    return res;
}

std::vector<std::string> command_manager::SAVEOF(const std::vector<std::string>&args, std::size_t index) {
    std::string ip=args[0];
    std::string port_str=args[1];
    auto port= convert<std::uint16_t>(port_str);
    asio::ip::tcp::socket socket(server->io_context);
    std::string wdata="SYNC ";
    wdata.append(server->acceptor.local_endpoint().address().to_string());
    wdata.append(" ");
    wdata.append(std::to_string(server->acceptor.local_endpoint().port()));
    socket.connect({asio::ip::make_address(ip),port});
    wdata= serialize(wdata);
    asio::write(socket,asio::buffer(wdata));
    std::string head(sizeof(std::uint64_t),'\0');
//    asio::read(socket,asio::buffer(head));
//    auto [len,_]=deserialize_head(head);
//    head.resize(len,'\0');
//    asio::read(socket,asio::buffer(head));
//    if(head!=ok){
//        return {head};
//    }
    redis_server::conn_ptr ptr=std::make_unique<redis_connection>(std::move(socket),server,server->conn_index);
    server->master=ptr.get();
    server->connections[server->conn_index++]=std::move(ptr);
    asio::co_spawn(server->run_context,server->master->start(),asio::detached);
    return {ok};
}

std::vector<std::string> command_manager::SENTI(const std::vector<std::string>&args , std::size_t index) {
    std::string ip=args[0],port_str=args[1];
    std::uint16_t port= convert<std::uint16_t>(args[1]);
    asio::ip::tcp::socket socket(server->io_context);
    socket.connect({asio::ip::make_address(ip),port});
    asio::co_spawn(server->run_context,server->senti.start(std::move(socket)),asio::detached);
    return {ok};
}

std::vector<std::string> command_manager::SELECT(const std::vector<std::string>&args, std::size_t index) {
    auto &conn = server->connections[index];
    std::size_t sindex= convert<std::size_t>(args[0]);
    if(sindex>=server->db_nums){
        return {nil};
    }
    conn->db_seq()=sindex;
    return {ok};
}

std::vector<std::string> command_manager::SYNC(const std::vector<std::string>&args, std::size_t index) {
    auto ptr=std::move(server->connections[index]);
    ptr->close_coro();
    server->slaves.add_slave(std::move(ptr->get_socket()),args[0]+' '+args[1]);
    return {ok};
}

std::vector<std::string> command_manager::PSENTI(const std::vector<std::string>&args, std::size_t index) {
    if(args[0]=="Tick"){
        auto conns=server->slaves.get_all_slave_ip_port();
        std::vector<std::string> res(conns.size());
        std::transform(conns.begin(),conns.end(),res.begin(),[]( decltype(*conns.begin())&tmp ){
            return tmp.first+" "+std::to_string(tmp.second);
        });
        return res;
    } else if(args[0]=="Ticket"){
        auto maxn= convert<std::size_t>(args[1]);
        std::random_device device;
        std::mt19937_64 mt(device());
        std::uniform_int_distribution<std::size_t> distribution(0,maxn);
        return {std::to_string(distribution(mt))};
    }
    return {nil};
}
